Load libraries
source("../General/rmb_functions.R")
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.2.9000 ✓ purrr 0.3.4
✓ tibble 3.0.1 ✓ dplyr 1.0.0
✓ tidyr 1.1.0 ✓ stringr 1.4.0
✓ readr 1.3.1 ✓ forcats 0.5.0
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
source("../General/parameters.R")
library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(cluster)
library(ggtree)
Registered S3 method overwritten by 'treeio':
method from
root.phylo ape
ggtree v1.16.6 For help: https://yulab-smu.github.io/treedata-book/
If you use ggtree in published research, please cite the most appropriate paper(s):
- Guangchuang Yu, Tommy Tsan-Yuk Lam, Huachen Zhu, Yi Guan. Two methods for mapping and visualizing associated data on phylogeny using ggtree. Molecular Biology and Evolution 2018, 35(12):3041-3043. doi: 10.1093/molbev/msy194
- Guangchuang Yu, David Smith, Huachen Zhu, Yi Guan, Tommy Tsan-Yuk Lam. ggtree: an R package for visualization and annotation of phylogenetic trees with their covariates and other associated data. Methods in Ecology and Evolution 2017, 8(1):28-36, doi:10.1111/2041-210X.12628
Attaching package: ‘ggtree’
The following object is masked from ‘package:tidyr’:
expand
library(cowplot)
********************************************************
Note: As of version 1.0.0, cowplot does not change the
default ggplot2 theme anymore. To recover the previous
behavior, execute:
theme_set(theme_cowplot())
********************************************************
library(tidyverse)
Load data
otu <- readRDS("../Data/otu_pers.RDS")
map <- readRDS("../Data/drought_map.RDS")
rs.fc <- readRDS("../Data/rs_shlfc.RDS")
es.fc <- readRDS("../Data/es_shlfc.RDS")
tax <- readRDS("../Data/tax.RDS") %>%
mutate(PhyClass2 = fct_recode(PhyClass2, "Low Abundance" = "other"))
Remove RF training set from mapping file. Subset mapping files for each compartment
map <- filter(map, Treatment2 != "WC_TRN")
rs.map <- filter(map, Compartment == "RS")
es.map <- filter(map, Compartment == "ES")
Get the relative abundances, reformat, and subset
otu <- rel_ab(otu)
rs.otu.tidy <- tidy_otu(otu) %>%
mutate(Count = Count/100) %>%
filter(SampleID %in% rs.map$SampleID) %>%
filter(!is.na(Count))
es.otu.tidy <- tidy_otu(otu) %>%
mutate(Count = Count/100) %>%
filter(SampleID %in% es.map$SampleID) %>%
filter(!is.na(Count))
Get significant OTUs for each set
rs.sig <- rs.fc %>%
select(-Day) %>%
separate(Contrast, c("Treatment", "Time"), sep = "\\.") %>%
filter(p.adjusted < 0.05) %>%
filter(Treatment != "WC_TRN")
es.sig <- es.fc %>%
select(-Day) %>%
separate(Contrast, c("Treatment", "Time"), sep = "\\.") %>%
filter(p.adjusted < 0.05) %>%
filter(Treatment != "WC_TRN")
For visualization purposes, generate a data frame with the range of drought stress time points for each treatment
trt.lines <- data.frame(Treatment = c("DS1", "DS2", "DS3"),
Treatment2 = c("DS1", "DS2", "DS3"),
Contrast = c("WC vs DS1", "WC vs DS2", "WC vs DS3"),
IniTreatment = c(4.5, 4.5, 4.5),
EndTreatment = c(5.5, 6.5, 7.5))
all.sig <- rbind(mutate(rs.sig, Compartment = "RS"),
mutate(es.sig, Compartment = "ES"))
dao.p <- all.sig %>%
mutate(Contrast = case_when(Treatment == "D1" ~ "WC vs DS1",
Treatment == "D2" ~ "WC vs DS2",
Treatment == "D3" ~ "WC vs DS3")) %>%
mutate(Compartment = ifelse(Compartment == "RS", "Rhizosphere", "Endosphere")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere")) %>%
ggplot(aes(as.numeric(Time) * 10)) +
geom_bar() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment * 10), linetype = 3) +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment * 10), linetype = 3) +
scale_fill_manual(values = resp.pal) +
xlab("Plant Age (days)") +
ylab("Number of OTUs") +
facet_grid(. ~ Compartment + Contrast) +
theme_classic() +
theme(text = element_text(size = 15))
dao.p

Define the clustering methods to test
m <- c( "average", "single", "complete", "ward")
names(m) <- c( "average", "single", "complete", "ward")
RHIZOSPHERE
z-transform relative abundances; calculate the mean z-score for each treatment, and time point combination; generate a matrix for hierarchical clustering
rs.zs.tidy <- rs.otu.tidy %>%
inner_join(map, by = "SampleID") %>%
filter(OTU_ID %in% rs.sig$OTU_ID) %>%
group_by(OTU_ID) %>%
mutate(zscore = (Count - mean(Count))/sd(Count)) %>%
group_by(Treatment, Time, OTU_ID, Age) %>%
summarise(MeanZS = mean(zscore)) %>%
ungroup()
`summarise()` regrouping output by 'Treatment', 'Time', 'OTU_ID' (override with `.groups` argument)
rs.zs.mtx <- rs.zs.tidy %>%
mutate(Group = paste(Treatment, Time, sep = ".")) %>%
select(Group, MeanZS, OTU_ID) %>%
spread(key = Group, value = MeanZS)
rs.zs.mtx <- as.data.frame(rs.zs.mtx)
rownames(rs.zs.mtx) <- rs.zs.mtx$OTU_ID
rs.zs.mtx <- rs.zs.mtx[,-1]
###################################################################
rs.fc.mtx <- rs.fc %>%
separate(Contrast, c("Treatment2", "Time"), sep = "\\.") %>%
mutate(Treatment = Treatment2) %>%
filter(Treatment2 != "WC_TRN") %>%
filter(OTU_ID %in% rs.sig$OTU_ID) %>%
mutate(Contrast = paste(Treatment2, Time, sep = ".")) %>%
select(Contrast, OTU_ID, estimate) %>%
spread(key = Contrast, value = estimate)
rs.fc.mtx <- as.data.frame(rs.fc.mtx)
rownames(rs.fc.mtx) <- rs.fc.mtx$OTU_ID
rs.fc.mtx <- rs.fc.mtx[,-1]
Choose a clustering method and number of clusters Approach based on: https://uc-r.github.io/hc_clustering
# See what values of k might be worth testing
fviz_nbclust(rs.zs.mtx, FUN = hcut, method = "wss")
Registered S3 method overwritten by 'data.table':
method from
print.data.table

# See which method gets you the best clustering
rs.ac <- function(x) {
agnes(rs.zs.mtx, method = x)$ac
}
map_dbl(m, rs.ac)
average single complete ward
0.5414037 0.3187765 0.7020742 0.9513128
###################################################################
# See what values of k might be worth testing
fviz_nbclust(rs.fc.mtx, FUN = hcut, method = "wss")

# See which method gets you the best clustering
rs.ac <- function(x) {
agnes(rs.fc.mtx, method = x)$ac
}
map_dbl(m, rs.ac)
average single complete ward
0.6296994 0.5583461 0.7871469 0.9502967
It seems the elbow is around 3 so test 2:4 by changing the value of the rs.k variable Perform hierarchical clustering and get the set of clusters and order of OTUs
rs.k <- 4
rs.dist <- dist(as.matrix(rs.zs.mtx))
rs.clust <- hclust(rs.dist, method = "ward.D")
rs.ord.names <- rs.clust$labels[rs.clust$order]
rs.ord <- data.frame(OTU_ID = rs.ord.names, order = 1:length(rs.ord.names))
rs.cut <- cutree(rs.clust[c(1,2,4)], k = rs.k)
rs.ord$Cluster <- as.factor(rs.cut[rs.ord$OTU_ID])
rs.ord <- rs.ord %>%
mutate(Cluster = paste("C", Cluster, sep = "")) %>%
group_by(Cluster) %>%
mutate(nOTU = n()) %>%
ungroup() %>%
mutate(Cluster2 = paste(Cluster, " (", nOTU, " OTUs)", sep = ""))
###################################################################
rs.k <- 4
rs.dist <- dist(as.matrix(rs.fc.mtx))
rs.clust <- hclust(rs.dist, method = "ward.D")
rs.ord.names <- rs.clust$labels[rs.clust$order]
rs.ord <- data.frame(OTU_ID = rs.ord.names, order = 1:length(rs.ord.names))
rs.cut <- cutree(rs.clust[c(1,2,4)], k = rs.k)
rs.ord$Cluster <- as.factor(rs.cut[rs.ord$OTU_ID])
rs.ord <- rs.ord %>%
mutate(Cluster = paste("C", Cluster, sep = "")) %>%
group_by(Cluster) %>%
mutate(nOTU = n()) %>%
ungroup() %>%
mutate(Cluster2 = paste(Cluster, " (", nOTU, " OTUs)", sep = ""))
Visualize as heatmap
rs.hm.p <- rs.zs.tidy %>%
inner_join(rs.ord, by = "OTU_ID") %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC")) %>%
ggplot(aes(Time*10, reorder(OTU_ID, order), fill = MeanZS)) +
geom_tile() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment*10), linetype = 3, color = "white") +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment*10), linetype = 3, color = "white") +
scale_fill_viridis_c(name = "Mean Rel. Abund.\n(z-score)") +
#scale_fill_distiller(palette = "Spectral") +
ylab("Differentially Abundant OTU") +
xlab("Plant Age (days)") +
facet_grid(Cluster ~ Treatment, scales = "free", space = "free") +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)
rs.hm.p

#################################
rs.fc %>%
separate(Contrast, c("Treatment2", "Time"), sep = "\\.") %>%
mutate(Treatment = Treatment2) %>%
filter(Treatment2 != "WC_TRN") %>%
mutate(Time = as.numeric(Time)) %>%
inner_join(rs.ord, by = "OTU_ID") %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC")) %>%
mutate(estimate2 = ifelse(abs(estimate) >5, 5 * sign(estimate), estimate)) %>%
ggplot(aes(Time*10, reorder(OTU_ID, order), fill = estimate2)) +
geom_tile() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment*10), linetype = 3, color = "white") +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment*10), linetype = 3, color = "white") +
#scale_fill_viridis_c(name = "Mean Rel. Abund.\n(z-score)") +
scale_fill_gradientn(name = "log2FC",
colors = RColorBrewer::brewer.pal(11, "BrBG")) +
#scale_fill_distiller(palette = "Spectral") +
ylab("Differentially Abundant OTU") +
xlab("Plant Age (days)") +
facet_grid(Cluster ~ Treatment, scales = "free", space = "free") +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)
Unknown levels in `f`: WC

Visualize the aggregated relative abundances
rs.agg.ra <- rs.otu.tidy %>%
inner_join(rs.ord, by = "OTU_ID") %>%
group_by(Cluster, SampleID) %>%
summarise(AggRelAb = sum(Count)) %>%
inner_join(rs.map, by = "SampleID") %>%
ungroup() %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC"))
`summarise()` regrouping output by 'Cluster' (override with `.groups` argument)
rs.agg.means <- rs.agg.ra %>%
group_by(Treatment, Compartment, Time, Cluster) %>%
mutate(MeanRelAb = mean(AggRelAb)) %>%
ungroup()
rs.clstr.p <- rs.agg.ra %>%
ggplot(aes(Age, AggRelAb, color = Treatment)) +
geom_point(stat = "identity", position = "dodge", alpha = 0.5, shape = 1) +
geom_line(data = rs.agg.means, aes(y = MeanRelAb), size = 1) +
geom_vline(xintercept = ws.line, linetype = 3) +
scale_color_manual(values = trt.pal, name = "Treatment") +
guides(color = guide_legend(ncol = 2)) +
ylab("Relative Abundance") +
xlab("Plant Age (days)") +
facet_wrap(~ Cluster, ncol = 1, scales = "free") +
theme_classic() +
theme(legend.position = "bottom",
text = element_text(size = 15))
rs.clstr.p

plot_grid(rs.hm.p, rs.clstr.p,
nrow = 1,
rel_widths = c(4,3),
align = "h",
axis = "b",
labels = c("A", "B"),
label_size = 20)
Width not defined. Set with `position_dodge(width = ?)`

ENDOSPHERE
z-transform relative abundances, calculate the mean z-score for each treatment, and time point combination, generate a matrix for hierarchical clustering
es.zs.tidy <- es.otu.tidy %>%
inner_join(map, by = "SampleID") %>%
filter(OTU_ID %in% es.sig$OTU_ID) %>%
group_by(OTU_ID) %>%
mutate(zscore = (Count - mean(Count))/sd(Count)) %>%
group_by(Treatment, Time, OTU_ID, Age) %>%
summarise(MeanZS = mean(zscore)) %>%
ungroup()
`summarise()` regrouping output by 'Treatment', 'Time', 'OTU_ID' (override with `.groups` argument)
es.zs.mtx <- es.zs.tidy %>%
mutate(Group = paste(Treatment, Time, sep = ".")) %>%
select(Group, MeanZS, OTU_ID) %>%
spread(key = Group, value = MeanZS)
es.zs.mtx <- as.data.frame(es.zs.mtx)
rownames(es.zs.mtx) <- es.zs.mtx$OTU_ID
es.zs.mtx <- es.zs.mtx[,-1]
###################################################################
es.fc.mtx <- es.fc %>%
separate(Contrast, c("Treatment2", "Time"), sep = "\\.") %>%
mutate(Treatment = Treatment2) %>%
filter(Treatment2 != "WC_TRN") %>%
filter(OTU_ID %in% es.sig$OTU_ID) %>%
mutate(Contrast = paste(Treatment2, Time, sep = ".")) %>%
select(Contrast, OTU_ID, estimate) %>%
spread(key = Contrast, value = estimate)
es.fc.mtx <- as.data.frame(es.fc.mtx)
rownames(es.fc.mtx) <- es.fc.mtx$OTU_ID
es.fc.mtx <- es.fc.mtx[,-1]
Choose a clustering method and number of clusters
fviz_nbclust(es.zs.mtx, FUN = hcut, method = "wss")

es.ac <- function(x) {
agnes(es.zs.mtx, method = x)$ac
}
map_dbl(m, es.ac)
average single complete ward
0.5642509 0.3817472 0.7234563 0.9242376
###################################################################
# See what values of k might be worth testing
fviz_nbclust(es.fc.mtx, FUN = hcut, method = "wss")

# See which method gets you the best clustering
es.ac <- function(x) {
agnes(es.fc.mtx, method = x)$ac
}
map_dbl(m, es.ac)
average single complete ward
0.6473055 0.5958731 0.7704513 0.9282877
Perform hierarchical clustering and get the set of clusters and order of OTUs
es.k <- 6
es.dist <- dist(as.matrix(es.zs.mtx))
es.clust <- hclust(es.dist, method = "ward.D")
es.ord.names <- es.clust$labels[es.clust$order]
es.ord <- data.frame(OTU_ID = es.ord.names, order = 1:length(es.ord.names))
es.cut <- cutree(es.clust[c(1,2,4)], k = es.k)
es.ord$Cluster <- as.factor(es.cut[es.ord$OTU_ID])
es.ord <- es.ord %>%
mutate(Cluster = paste("C", Cluster, sep = "")) %>%
group_by(Cluster) %>%
mutate(nOTU = n()) %>%
ungroup() %>%
mutate(Cluster2 = paste(Cluster, " (", nOTU, " OTUs)", sep = ""))
###################################################################
es.k <- 5
es.dist <- dist(as.matrix(es.fc.mtx))
es.clust <- hclust(es.dist, method = "ward.D")
es.ord.names <- es.clust$labels[es.clust$order]
es.ord <- data.frame(OTU_ID = es.ord.names, order = 1:length(es.ord.names))
es.cut <- cutree(es.clust[c(1,2,4)], k = es.k)
es.ord$Cluster <- as.factor(es.cut[es.ord$OTU_ID])
es.ord <- es.ord %>%
mutate(Cluster = paste("C", Cluster, sep = "")) %>%
group_by(Cluster) %>%
mutate(nOTU = n()) %>%
ungroup() %>%
mutate(Cluster2 = paste(Cluster, " (", nOTU, " OTUs)", sep = ""))
Visualize as heatmap
es.hm.p <- es.zs.tidy %>%
inner_join(es.ord, by = "OTU_ID") %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC")) %>%
ggplot(aes(Time*10, reorder(OTU_ID, order), fill = MeanZS)) +
geom_tile() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment*10), linetype = 3, color = "white") +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment*10), linetype = 3, color = "white") +
scale_fill_viridis_c(name = "Mean Rel. Abund.\n(z-score)") +
#scale_fill_distiller(palette = "Spectral") +
ylab("Differentially Abundant OTU") +
xlab("Plant Age (days)") +
facet_grid(Cluster ~ Treatment, scales = "free", space = "free") +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)
es.hm.p

#################################
es.fc %>%
separate(Contrast, c("Treatment2", "Time"), sep = "\\.") %>%
mutate(Treatment = Treatment2) %>%
filter(Treatment2 != "WC_TRN") %>%
mutate(Time = as.numeric(Time)) %>%
inner_join(es.ord, by = "OTU_ID") %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC")) %>%
mutate(estimate2 = ifelse(abs(estimate) >5, 5 * sign(estimate), estimate)) %>%
ggplot(aes(Time*10, reorder(OTU_ID, order), fill = estimate2)) +
geom_tile() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment*10), linetype = 3, color = "white") +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment*10), linetype = 3, color = "white") +
#scale_fill_viridis_c(name = "Mean Rel. Abund.\n(z-score)") +
scale_fill_gradientn(name = "log2FC",
colors = RColorBrewer::brewer.pal(11, "BrBG")) +
#scale_fill_distiller(palette = "Spectral") +
ylab("Differentially Abundant OTU") +
xlab("Plant Age (days)") +
facet_grid(Cluster ~ Treatment, scales = "free", space = "free") +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)
Unknown levels in `f`: WC

Visualize the aggregated relative abundances
es.agg.ra <- es.otu.tidy %>%
inner_join(es.ord, by = "OTU_ID") %>%
group_by(Cluster, SampleID) %>%
summarise(AggRelAb = sum(Count)) %>%
inner_join(es.map, by = "SampleID") %>%
ungroup() %>%
mutate(Treatment = str_replace(Treatment, "D", "DS")) %>%
mutate(Treatment = fct_relevel(Treatment, "WC"))
`summarise()` regrouping output by 'Cluster' (override with `.groups` argument)
es.agg.means <- es.agg.ra %>%
group_by(Treatment, Compartment, Time, Cluster) %>%
mutate(MeanRelAb = mean(AggRelAb)) %>%
ungroup()
es.clstr.p <- es.agg.ra %>%
ggplot(aes(Age, AggRelAb, color = Treatment)) +
geom_point(stat = "identity", position = "dodge", alpha = 0.5, shape = 1) +
geom_line(data = es.agg.means, aes(y = MeanRelAb), size = 1) +
geom_vline(xintercept = ws.line, linetype = 3) +
scale_color_manual(values = trt.pal, name = "Treatment") +
guides(color = guide_legend(ncol = 2)) +
ylab("Relative Abundance") +
xlab("Plant Age (days)") +
facet_wrap(~ Cluster, ncol = 1, scales = "free") +
theme_classic() +
theme(legend.position = "bottom",
text = element_text(size = 15))
es.clstr.p

plot_grid(es.hm.p, es.clstr.p,
nrow = 1,
rel_widths = c(3,2),
align = "h",
axis = "b",
labels = c("A", "B"),
label_size = 20)
Width not defined. Set with `position_dodge(width = ?)`

all.ord <- rbind(mutate(rs.ord, Compartment = "RS"),
mutate(es.ord, Compartment = "ES"))
rbind(mutate(rs.fc, Compartment = "RS"),
mutate(es.fc, Compartment = "ES")) %>%
separate(Contrast, c("Treatment2", "Time"), sep = "\\.", remove = F) %>%
mutate(Treatment = Treatment2) %>%
filter(Treatment2 != "WC_TRN") %>%
mutate(Time = as.numeric(Time)) %>%
inner_join(all.ord, by = c("Compartment", "OTU_ID")) %>%
mutate(Contrast = case_when(Treatment == "D1" ~ "WC vs DS1",
Treatment == "D2" ~ "WC vs DS2",
Treatment == "D3" ~ "WC vs DS3")) %>%
mutate(Compartment = fct_relevel(Compartment, "RS")) %>%
mutate(estimate2 = ifelse(abs(estimate) >4, 4 * sign(estimate), estimate)) %>%
ggplot(aes(Time*10, reorder(OTU_ID, order), fill = estimate2)) +
geom_tile() +
geom_vline(data = trt.lines, aes(xintercept = IniTreatment * 10), linetype = 3) +
geom_vline(data = trt.lines, aes(xintercept = EndTreatment * 10), linetype = 3) +
#scale_fill_viridis_c(name = "Mean Rel. Abund.\n(z-score)") +
scale_fill_gradientn(name = "log2FC",
colors = RColorBrewer::brewer.pal(11, "BrBG")) +
#scale_fill_distiller(palette = "Spectral") +
ylab("Differentially Abundant OTU") +
xlab("Plant Age (days)") +
facet_grid(Compartment + Cluster ~ Contrast, scales = "free", space = "free") +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)

all.ord %>%
inner_join(tax, by = "OTU_ID") %>%
ggplot(aes(1, fill = PhyClass2)) +
geom_bar(position = "stack") +
facet_grid(Compartment + Cluster ~ ., scales = "free", space = "free") +
scale_fill_manual(name = "Taxon",
values = phy.pal[c(11:14,2:10,1)],
limits = levels(tax$PhyClass2)[c(11:14,2:10,1)]) +
theme_classic() +
theme(text = element_text(size = 15),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
legend.position = "bottom",
legend.title.align = 1)

Generate a data frame with all clustering results, and label the interesting clusters for downstreama analyses Genearte a data frame with all the mean z-scores for plotting
all.clust.summary <- rbind(mutate(rs.ord, Compartment = "RS"),
mutate(es.ord, Compartment = "ES")) %>%
mutate(Cluster = paste(Compartment, Cluster, sep = "")) %>%
filter(Cluster %in% c("RSC1", "RSC2", "RSC4", "ESC3", "ESC5")) %>%
mutate(Trend = case_when(Cluster == "RSC1" ~ "Transient Enrichment",
Cluster == "RSC2" ~ "Persistent Depletion",
Cluster == "RSC4" ~ "Transient Depletion",
Cluster == "ESC3" ~ "Semi-Persistent Enrichment",
Cluster == "ESC5" ~ "Persistent Depletion"))
all.cluster.total <- all.clust.summary %>%
group_by(Cluster, nOTU, Cluster2, Compartment, Trend) %>%
count() %>%
select(-n) %>%
ungroup() %>%
mutate(Compartment = fct_recode(Compartment,
"Rhizosphere" = "RS",
"Endosphere" = "ES")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere"))
all.agg.ra <- rbind(mutate(rs.agg.ra, Compartment = "RS"),
mutate(es.agg.ra, Compartment = "ES")) %>%
mutate(Cluster = paste(Compartment, Cluster, sep = ""))
saveRDS(all.clust.summary, "../Data/drought_clusters.RDS")
###############################################################################
all.clust.summary <- rbind(mutate(rs.ord, Compartment = "RS"),
mutate(es.ord, Compartment = "ES")) %>%
mutate(Cluster = paste(Compartment, Cluster, sep = "")) %>%
filter(Cluster %in% c("RSC1", "RSC2", "RSC4", "ESC3", "ESC4")) %>%
mutate(Trend = case_when(Cluster == "RSC1" ~ "Transient Enrichment",
Cluster == "RSC2" ~ "Transient Depletion",
Cluster == "RSC4" ~ "Persistent Depletion",
Cluster == "ESC3" ~ "Semi-Persistent Enrichment",
Cluster == "ESC4" ~ "Persistent Depletion"))
all.cluster.total <- all.clust.summary %>%
group_by(Cluster, nOTU, Cluster2, Compartment, Trend) %>%
count() %>%
select(-n) %>%
ungroup() %>%
mutate(Compartment = fct_recode(Compartment,
"Rhizosphere" = "RS",
"Endosphere" = "ES")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere"))
all.agg.ra <- rbind(mutate(rs.agg.ra, Compartment = "RS"),
mutate(es.agg.ra, Compartment = "ES")) %>%
mutate(Cluster = paste(Compartment, Cluster, sep = ""))
saveRDS(all.clust.summary, "../Data/drought_clusters.RDS")
all.cluster.means <- all.agg.ra %>%
mutate(Compartment = fct_recode(Compartment,
"Rhizosphere" = "RS",
"Endosphere" = "ES")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere")) %>%
inner_join(select(all.cluster.total, -Compartment), by = "Cluster") %>%
group_by(Compartment, Time, Age, Treatment2, Trend) %>%
summarise(MeanRelAb = mean(AggRelAb)) %>%
ungroup() %>%
mutate(Treatment2 = str_replace(Treatment2, "D", "DS")) %>%
mutate(Treatment2 = fct_relevel(Treatment2, "WC"))
`summarise()` regrouping output by 'Compartment', 'Time', 'Age', 'Treatment2' (override with `.groups` argument)
all.cluster.ab <- all.agg.ra %>%
mutate(Compartment = fct_recode(Compartment,
"Rhizosphere" = "RS",
"Endosphere" = "ES")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere")) %>%
inner_join(select(all.cluster.total, -Compartment), by = "Cluster") %>%
mutate(Treatment2 = str_replace(Treatment2, "D", "DS")) %>%
mutate(Treatment2 = fct_relevel(Treatment2, "WC"))
all.cluster.tax <- all.clust.summary %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere")) %>%
group_by(Compartment, Cluster, OTU_ID) %>%
count() %>%
ungroup() %>%
inner_join(tax, by = "OTU_ID") %>%
inner_join(select(all.clust.summary, OTU_ID, Trend, Compartment), by = c("Compartment", "OTU_ID")) %>%
mutate(Compartment = fct_recode(Compartment,
"Rhizosphere" = "RS",
"Endosphere" = "ES")) %>%
mutate(Compartment = fct_relevel(Compartment, "Rhizosphere"))
Unknown levels in `f`: Rhizosphere
all.ab <- all.cluster.ab %>%
ggplot(aes(Age, AggRelAb, color = Treatment2)) +
geom_point(alpha = 1, shape = 1, size = 2) +
geom_vline(xintercept = ws.line, linetype = 3) +
geom_line(data = all.cluster.means, aes(y = MeanRelAb), size = 1) +
ylab("Cumulative Relative Abundance") +
xlab("Plant Age (days)") +
facet_wrap(~ Compartment + Trend, scales = "free") +
scale_color_manual(values = trt.pal, name = "Treatment") +
guides(color = guide_legend(title.position = "top", title.hjust = 0.5)) +
theme_classic() +
theme(legend.position = c(0.8,0.2),
text = element_text(size = 15))
all.ab

all.tax.p <- all.cluster.tax %>%
group_by(Compartment, Trend, PhyClass2) %>%
summarise(Count = sum(n)) %>%
group_by(Compartment, Trend) %>%
mutate(Fraction = Count / sum(Count)) %>%
mutate(ymax = cumsum(Fraction),
nPhy = n()) %>%
mutate(ymin = c(0, ymax[1:nPhy - 1])) %>%
ggplot() +
geom_rect(aes(ymax=ymax, ymin=ymin, xmax= 4, xmin= 3, fill= PhyClass2)) +
geom_text(data = all.cluster.total, aes(2, 0, label = nOTU), size = 7) +
scale_fill_manual(name = "Taxon",
values = phy.pal[c(11:14,2:10,1)],
limits = levels(tax$PhyClass2)[c(11:14,2:10,1)]) +
guides(fill = guide_legend(ncol = 4)) +
coord_polar(theta="y") +
xlim(c(2, 4)) +
facet_wrap(. ~ Compartment + Trend, nrow = 1) +
theme_void() +
theme(text = element_text(size = 15),
legend.position = "bottom",
legend.title = element_blank(),
strip.background = element_blank(),
strip.text = element_blank())
`summarise()` regrouping output by 'Compartment', 'Trend' (override with `.groups` argument)
numerical expression has 7 elements: only the first usednumerical expression has 12 elements: only the first usednumerical expression has 14 elements: only the first usednumerical expression has 11 elements: only the first usednumerical expression has 3 elements: only the first used
all.tax.p

all.clust.p <- plot_grid(all.ab, all.tax.p,
ncol = 1,
rel_heights = c(6,4),
align = "v",
axis = "lr",
labels = "B",
label_size = 20)
number of items to replace is not a multiple of replacement length
plot_grid(dao.p, all.ab, all.tax.p,
ncol = 1,
rel_heights = c(1,2,1),
labels = c("A", "B", NA),
label_size = 20) ###766:936
number of items to replace is not a multiple of replacement length

all.cluster.tax %>%
group_by(Compartment, Trend, Phylum, PhyClass2, Class, Order) %>%
count() %>%
filter(n > 1) %>%
mutate(Taxonomy = paste(Phylum, Class, Order, sep = " / ")) %>%
ggplot(aes(Trend, Taxonomy, fill = PhyClass2)) +
geom_tile(color = "white", size = 1) +
geom_text(aes(label = n)) +
scale_fill_manual(name = "Taxon",
values = phy.pal,
limits = levels(tax$PhyClass2)) +
facet_grid(. ~ Compartment, scales = "free", space = "free") +
theme_minimal() +
theme(text = element_text(size = 15),
axis.text.x = element_text(angle = 45, hjust = 1))
Using `n` as weighting variable
ℹ Quiet this message with `wt = n` or count rows with `wt = 1`

LS0tCnRpdGxlOiAiT1RVIENsdXN0ZXJpbmciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkxvYWQgbGlicmFyaWVzCmBgYHtyfQpzb3VyY2UoIi4uL0dlbmVyYWwvcm1iX2Z1bmN0aW9ucy5SIikKc291cmNlKCIuLi9HZW5lcmFsL3BhcmFtZXRlcnMuUiIpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShjbHVzdGVyKQpsaWJyYXJ5KGdndHJlZSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpMb2FkIGRhdGEKYGBge3J9Cm90dSA8LSByZWFkUkRTKCIuLi9EYXRhL290dV9wZXJzLlJEUyIpCm1hcCA8LSByZWFkUkRTKCIuLi9EYXRhL2Ryb3VnaHRfbWFwLlJEUyIpCnJzLmZjIDwtIHJlYWRSRFMoIi4uL0RhdGEvcnNfc2hsZmMuUkRTIikKZXMuZmMgPC0gcmVhZFJEUygiLi4vRGF0YS9lc19zaGxmYy5SRFMiKQp0YXggPC0gcmVhZFJEUygiLi4vRGF0YS90YXguUkRTIikgJT4lIAogIG11dGF0ZShQaHlDbGFzczIgPSBmY3RfcmVjb2RlKFBoeUNsYXNzMiwgIkxvdyBBYnVuZGFuY2UiID0gIm90aGVyIikpCmBgYAoKUmVtb3ZlIFJGIHRyYWluaW5nIHNldCBmcm9tIG1hcHBpbmcgZmlsZS4gU3Vic2V0IG1hcHBpbmcgZmlsZXMgZm9yIGVhY2ggY29tcGFydG1lbnQKYGBge3J9Cm1hcCA8LSBmaWx0ZXIobWFwLCBUcmVhdG1lbnQyICE9ICJXQ19UUk4iKQpycy5tYXAgPC0gZmlsdGVyKG1hcCwgQ29tcGFydG1lbnQgPT0gIlJTIikKZXMubWFwIDwtIGZpbHRlcihtYXAsIENvbXBhcnRtZW50ID09ICJFUyIpCmBgYAoKR2V0IHRoZSByZWxhdGl2ZSBhYnVuZGFuY2VzLCByZWZvcm1hdCwgYW5kIHN1YnNldApgYGB7cn0Kb3R1IDwtIHJlbF9hYihvdHUpCgpycy5vdHUudGlkeSA8LSB0aWR5X290dShvdHUpICU+JSAKICBtdXRhdGUoQ291bnQgPSBDb3VudC8xMDApICU+JSAKICBmaWx0ZXIoU2FtcGxlSUQgJWluJSBycy5tYXAkU2FtcGxlSUQpICU+JSAKICBmaWx0ZXIoIWlzLm5hKENvdW50KSkgCgplcy5vdHUudGlkeSA8LSB0aWR5X290dShvdHUpICU+JSAKICBtdXRhdGUoQ291bnQgPSBDb3VudC8xMDApICU+JSAKICBmaWx0ZXIoU2FtcGxlSUQgJWluJSBlcy5tYXAkU2FtcGxlSUQpICU+JSAKICBmaWx0ZXIoIWlzLm5hKENvdW50KSkgCmBgYAoKR2V0IHNpZ25pZmljYW50IE9UVXMgZm9yIGVhY2ggc2V0CmBgYHtyfQpycy5zaWcgPC0gcnMuZmMgJT4lIAogIHNlbGVjdCgtRGF5KSAlPiUgCiAgc2VwYXJhdGUoQ29udHJhc3QsIGMoIlRyZWF0bWVudCIsICJUaW1lIiksIHNlcCA9ICJcXC4iKSAlPiUgCiAgZmlsdGVyKHAuYWRqdXN0ZWQgPCAwLjA1KSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudCAhPSAiV0NfVFJOIikgCgplcy5zaWcgPC0gZXMuZmMgJT4lIAogIHNlbGVjdCgtRGF5KSAlPiUgCiAgc2VwYXJhdGUoQ29udHJhc3QsIGMoIlRyZWF0bWVudCIsICJUaW1lIiksIHNlcCA9ICJcXC4iKSAlPiUgCiAgZmlsdGVyKHAuYWRqdXN0ZWQgPCAwLjA1KSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudCAhPSAiV0NfVFJOIikgCmBgYAoKRm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMsIGdlbmVyYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSByYW5nZSBvZiBkcm91Z2h0IHN0cmVzcyB0aW1lIHBvaW50cyBmb3IgZWFjaCB0cmVhdG1lbnQgCmBgYHtyfQp0cnQubGluZXMgPC0gZGF0YS5mcmFtZShUcmVhdG1lbnQgPSBjKCJEUzEiLCAiRFMyIiwgIkRTMyIpLAogICAgICAgICAgICAgICAgICAgICAgICBUcmVhdG1lbnQyID0gYygiRFMxIiwgIkRTMiIsICJEUzMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgQ29udHJhc3QgPSBjKCJXQyB2cyBEUzEiLCAiV0MgdnMgRFMyIiwgIldDIHZzIERTMyIpLAogICAgICAgICAgICAgICAgICAgICAgICBJbmlUcmVhdG1lbnQgPSBjKDQuNSwgNC41LCA0LjUpLAogICAgICAgICAgICAgICAgICAgICAgICBFbmRUcmVhdG1lbnQgPSBjKDUuNSwgNi41LCA3LjUpKQpgYGAKCgpgYGB7cn0KYWxsLnNpZyA8LSByYmluZChtdXRhdGUocnMuc2lnLCBDb21wYXJ0bWVudCA9ICJSUyIpLAogICAgICAgICAgICAgICAgIG11dGF0ZShlcy5zaWcsIENvbXBhcnRtZW50ID0gIkVTIikpCgpkYW8ucCA8LSBhbGwuc2lnICU+JSAKICBtdXRhdGUoQ29udHJhc3QgPSBjYXNlX3doZW4oVHJlYXRtZW50ID09ICJEMSIgfiAiV0MgdnMgRFMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID09ICJEMiIgfiAiV0MgdnMgRFMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID09ICJEMyIgfiAiV0MgdnMgRFMzIikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBpZmVsc2UoQ29tcGFydG1lbnQgPT0gIlJTIiwgIlJoaXpvc3BoZXJlIiwgIkVuZG9zcGhlcmUiKSkgJT4lIAogIG11dGF0ZShDb21wYXJ0bWVudCA9IGZjdF9yZWxldmVsKENvbXBhcnRtZW50LCAiUmhpem9zcGhlcmUiKSkgJT4lIAogIGdncGxvdChhZXMoYXMubnVtZXJpYyhUaW1lKSAqIDEwKSkgKwogIGdlb21fYmFyKCkgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBJbmlUcmVhdG1lbnQgKiAxMCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBFbmRUcmVhdG1lbnQgKiAxMCksIGxpbmV0eXBlID0gMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHJlc3AucGFsKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICB5bGFiKCJOdW1iZXIgb2YgT1RVcyIpICsKICBmYWNldF9ncmlkKC4gfiBDb21wYXJ0bWVudCArIENvbnRyYXN0KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCgpkYW8ucApgYGAKYGBge3J9CnRpbWUuYWdlIDwtIG1hcCAlPiUgCiAgZ3JvdXBfYnkoVGltZSwgQWdlKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBzZWxlY3QoLW4pCgpkYW8uc3VwcCA8LSBhbGwuc2lnICU+JSAKICBtdXRhdGUoVGltZSA9IGFzLmludGVnZXIoVGltZSkpICU+JSAKICBtdXRhdGUoQ29udHJhc3QgPSBwYXN0ZSgiV0NfdnNfIiwgVHJlYXRtZW50LCBzZXAgPSAiIikpICU+JSAKICBzZWxlY3QoLVRyZWF0bWVudCkgJT4lIAogIGlubmVyX2pvaW4odGltZS5hZ2UsIGJ5ID0gIlRpbWUiKSAlPiUgCiAgc2VsZWN0KENvbXBhcnRtZW50LCBDb250cmFzdCwgVGltZSwgQWdlLCBldmVyeXRoaW5nKCkpICU+JSAKICBpbm5lcl9qb2luKHRheCwgYnkgPSAiT1RVX0lEIikgJT4lIAogIHNlbGVjdCgtUGh5Q2xhc3MsIC1QaHlDbGFzczIpCgp3cml0ZS50YWJsZShkYW8uc3VwcCwgIi4uL1RhYmxlcy9kYW8udHN2Iiwgc2VwID0gIlx0IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCgpEZWZpbmUgdGhlIGNsdXN0ZXJpbmcgbWV0aG9kcyB0byB0ZXN0CmBgYHtyfQptIDwtIGMoICJhdmVyYWdlIiwgInNpbmdsZSIsICJjb21wbGV0ZSIsICJ3YXJkIikKbmFtZXMobSkgPC0gYyggImF2ZXJhZ2UiLCAic2luZ2xlIiwgImNvbXBsZXRlIiwgIndhcmQiKQpgYGAKCgpSSElaT1NQSEVSRQoKei10cmFuc2Zvcm0gcmVsYXRpdmUgYWJ1bmRhbmNlczsgY2FsY3VsYXRlIHRoZSBtZWFuIHotc2NvcmUgZm9yIGVhY2ggdHJlYXRtZW50LCBhbmQgdGltZSBwb2ludCBjb21iaW5hdGlvbjsgZ2VuZXJhdGUgYSBtYXRyaXggZm9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmBgYHtyfQpycy56cy50aWR5IDwtIHJzLm90dS50aWR5ICU+JQogIGlubmVyX2pvaW4obWFwLCBieSA9ICJTYW1wbGVJRCIpICU+JQogIGZpbHRlcihPVFVfSUQgJWluJSBycy5zaWckT1RVX0lEKSAlPiUKICBncm91cF9ieShPVFVfSUQpICU+JQogIG11dGF0ZSh6c2NvcmUgPSAoQ291bnQgLSBtZWFuKENvdW50KSkvc2QoQ291bnQpKSAlPiUKICBncm91cF9ieShUcmVhdG1lbnQsIFRpbWUsIE9UVV9JRCwgQWdlKSAlPiUKICBzdW1tYXJpc2UoTWVhblpTID0gbWVhbih6c2NvcmUpKSAlPiUKICB1bmdyb3VwKCkKCnJzLnpzLm10eCA8LSBycy56cy50aWR5ICU+JSAKICAgIG11dGF0ZShHcm91cCA9IHBhc3RlKFRyZWF0bWVudCwgVGltZSwgc2VwID0gIi4iKSkgJT4lIAogICAgc2VsZWN0KEdyb3VwLCBNZWFuWlMsIE9UVV9JRCkgJT4lIAogICAgc3ByZWFkKGtleSA9IEdyb3VwLCB2YWx1ZSA9IE1lYW5aUykKcnMuenMubXR4IDwtIGFzLmRhdGEuZnJhbWUocnMuenMubXR4KSAKcm93bmFtZXMocnMuenMubXR4KSA8LSBycy56cy5tdHgkT1RVX0lEIApycy56cy5tdHggPC0gcnMuenMubXR4WywtMV0gCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpycy5mYy5tdHggPC0gcnMuZmMgJT4lIAogIHNlcGFyYXRlKENvbnRyYXN0LCBjKCJUcmVhdG1lbnQyIiwgIlRpbWUiKSwgc2VwID0gIlxcLiIpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gVHJlYXRtZW50MikgJT4lIAogIGZpbHRlcihUcmVhdG1lbnQyICE9ICJXQ19UUk4iKSAlPiUgCiAgZmlsdGVyKE9UVV9JRCAlaW4lIHJzLnNpZyRPVFVfSUQpICU+JSAKICBtdXRhdGUoQ29udHJhc3QgPSBwYXN0ZShUcmVhdG1lbnQyLCBUaW1lLCBzZXAgPSAiLiIpKSAlPiUgCiAgc2VsZWN0KENvbnRyYXN0LCBPVFVfSUQsIGVzdGltYXRlKSAlPiUgCiAgc3ByZWFkKGtleSA9IENvbnRyYXN0LCB2YWx1ZSA9IGVzdGltYXRlKQpycy5mYy5tdHggPC0gYXMuZGF0YS5mcmFtZShycy5mYy5tdHgpIApyb3duYW1lcyhycy5mYy5tdHgpIDwtIHJzLmZjLm10eCRPVFVfSUQgCnJzLmZjLm10eCA8LSBycy5mYy5tdHhbLC0xXSAKYGBgCgpDaG9vc2UgYSBjbHVzdGVyaW5nIG1ldGhvZCBhbmQgbnVtYmVyIG9mIGNsdXN0ZXJzCkFwcHJvYWNoIGJhc2VkIG9uOiBodHRwczovL3VjLXIuZ2l0aHViLmlvL2hjX2NsdXN0ZXJpbmcKYGBge3J9CiMgU2VlIHdoYXQgdmFsdWVzIG9mIGsgbWlnaHQgYmUgd29ydGggdGVzdGluZwpmdml6X25iY2x1c3QocnMuenMubXR4LCBGVU4gPSBoY3V0LCBtZXRob2QgPSAid3NzIikKCiMgU2VlIHdoaWNoIG1ldGhvZCBnZXRzIHlvdSB0aGUgYmVzdCBjbHVzdGVyaW5nCnJzLmFjIDwtIGZ1bmN0aW9uKHgpIHsKICBhZ25lcyhycy56cy5tdHgsIG1ldGhvZCA9IHgpJGFjCn0KbWFwX2RibChtLCBycy5hYykKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU2VlIHdoYXQgdmFsdWVzIG9mIGsgbWlnaHQgYmUgd29ydGggdGVzdGluZwpmdml6X25iY2x1c3QocnMuZmMubXR4LCBGVU4gPSBoY3V0LCBtZXRob2QgPSAid3NzIikKCiMgU2VlIHdoaWNoIG1ldGhvZCBnZXRzIHlvdSB0aGUgYmVzdCBjbHVzdGVyaW5nCnJzLmFjIDwtIGZ1bmN0aW9uKHgpIHsKICBhZ25lcyhycy5mYy5tdHgsIG1ldGhvZCA9IHgpJGFjCn0KbWFwX2RibChtLCBycy5hYykKYGBgCgpJdCBzZWVtcyB0aGUgZWxib3cgaXMgYXJvdW5kIDMgc28gdGVzdCAyOjQgYnkgY2hhbmdpbmcgdGhlIHZhbHVlIG9mIHRoZSBycy5rIHZhcmlhYmxlClBlcmZvcm0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgYW5kIGdldCB0aGUgc2V0IG9mIGNsdXN0ZXJzIGFuZCBvcmRlciBvZiBPVFVzCmBgYHtyfQpycy5rIDwtIDQKCnJzLmRpc3QgPC0gZGlzdChhcy5tYXRyaXgocnMuenMubXR4KSkgCnJzLmNsdXN0IDwtIGhjbHVzdChycy5kaXN0LCBtZXRob2QgPSAid2FyZC5EIikgCnJzLm9yZC5uYW1lcyA8LSBycy5jbHVzdCRsYWJlbHNbcnMuY2x1c3Qkb3JkZXJdIApycy5vcmQgPC0gZGF0YS5mcmFtZShPVFVfSUQgPSBycy5vcmQubmFtZXMsIG9yZGVyID0gMTpsZW5ndGgocnMub3JkLm5hbWVzKSkKCnJzLmN1dCA8LSBjdXRyZWUocnMuY2x1c3RbYygxLDIsNCldLCBrID0gcnMuaykKcnMub3JkJENsdXN0ZXIgPC0gYXMuZmFjdG9yKHJzLmN1dFtycy5vcmQkT1RVX0lEXSkKCnJzLm9yZCA8LSBycy5vcmQgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoIkMiLCBDbHVzdGVyLCBzZXAgPSAiIikpICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgbXV0YXRlKG5PVFUgPSBuKCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShDbHVzdGVyMiA9IHBhc3RlKENsdXN0ZXIsICIgKCIsIG5PVFUsICIgT1RVcykiLCBzZXAgPSAiIikpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpycy5rIDwtIDQKCnJzLmRpc3QgPC0gZGlzdChhcy5tYXRyaXgocnMuZmMubXR4KSkgCnJzLmNsdXN0IDwtIGhjbHVzdChycy5kaXN0LCBtZXRob2QgPSAid2FyZC5EIikgCnJzLm9yZC5uYW1lcyA8LSBycy5jbHVzdCRsYWJlbHNbcnMuY2x1c3Qkb3JkZXJdIApycy5vcmQgPC0gZGF0YS5mcmFtZShPVFVfSUQgPSBycy5vcmQubmFtZXMsIG9yZGVyID0gMTpsZW5ndGgocnMub3JkLm5hbWVzKSkKCnJzLmN1dCA8LSBjdXRyZWUocnMuY2x1c3RbYygxLDIsNCldLCBrID0gcnMuaykKcnMub3JkJENsdXN0ZXIgPC0gYXMuZmFjdG9yKHJzLmN1dFtycy5vcmQkT1RVX0lEXSkKCnJzLm9yZCA8LSBycy5vcmQgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoIkMiLCBDbHVzdGVyLCBzZXAgPSAiIikpICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgbXV0YXRlKG5PVFUgPSBuKCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShDbHVzdGVyMiA9IHBhc3RlKENsdXN0ZXIsICIgKCIsIG5PVFUsICIgT1RVcykiLCBzZXAgPSAiIikpCmBgYAoKVmlzdWFsaXplIGFzIGhlYXRtYXAKYGBge3J9CnJzLmhtLnAgPC0gcnMuenMudGlkeSAlPiUgCiAgaW5uZXJfam9pbihycy5vcmQsIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gc3RyX3JlcGxhY2UoVHJlYXRtZW50LCAiRCIsICJEUyIpKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IGZjdF9yZWxldmVsKFRyZWF0bWVudCwgIldDIikpICU+JSAKICBnZ3Bsb3QoYWVzKFRpbWUqMTAsIHJlb3JkZXIoT1RVX0lELCBvcmRlciksIGZpbGwgPSBNZWFuWlMpKSArCiAgZ2VvbV90aWxlKCkgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBJbmlUcmVhdG1lbnQqMTApLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBFbmRUcmVhdG1lbnQqMTApLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiTWVhbiBSZWwuIEFidW5kLlxuKHotc2NvcmUpIikgKwogICNzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogIHlsYWIoIkRpZmZlcmVudGlhbGx5IEFidW5kYW50IE9UVSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X2dyaWQoQ2x1c3RlciB+IFRyZWF0bWVudCwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAxKQoKcnMuaG0ucAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnJzLmZjICU+JSAKICBzZXBhcmF0ZShDb250cmFzdCwgYygiVHJlYXRtZW50MiIsICJUaW1lIiksIHNlcCA9ICJcXC4iKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IFRyZWF0bWVudDIpICU+JSAKICBmaWx0ZXIoVHJlYXRtZW50MiAhPSAiV0NfVFJOIikgJT4lIAogIG11dGF0ZShUaW1lID0gYXMubnVtZXJpYyhUaW1lKSkgJT4lIAogIGlubmVyX2pvaW4ocnMub3JkLCBieSA9ICJPVFVfSUQiKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IHN0cl9yZXBsYWNlKFRyZWF0bWVudCwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKSAlPiUgCiAgbXV0YXRlKGVzdGltYXRlMiA9IGlmZWxzZShhYnMoZXN0aW1hdGUpID41LCA1ICogc2lnbihlc3RpbWF0ZSksIGVzdGltYXRlKSkgJT4lIAogIGdncGxvdChhZXMoVGltZSoxMCwgcmVvcmRlcihPVFVfSUQsIG9yZGVyKSwgZmlsbCA9IGVzdGltYXRlMikpICsKICBnZW9tX3RpbGUoKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEluaVRyZWF0bWVudCoxMCksIGxpbmV0eXBlID0gMywgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEVuZFRyZWF0bWVudCoxMCksIGxpbmV0eXBlID0gMywgY29sb3IgPSAid2hpdGUiKSArCiAgI3NjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiTWVhbiBSZWwuIEFidW5kLlxuKHotc2NvcmUpIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKG5hbWUgPSAibG9nMkZDIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMTEsICJCckJHIikpICsKICAjc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICB5bGFiKCJEaWZmZXJlbnRpYWxseSBBYnVuZGFudCBPVFUiKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICBmYWNldF9ncmlkKENsdXN0ZXIgfiBUcmVhdG1lbnQsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlLmFsaWduID0gMSkKYGBgCgpWaXN1YWxpemUgdGhlIGFnZ3JlZ2F0ZWQgcmVsYXRpdmUgYWJ1bmRhbmNlcwpgYGB7cn0KcnMuYWdnLnJhIDwtIHJzLm90dS50aWR5ICU+JSAKICBpbm5lcl9qb2luKHJzLm9yZCwgYnkgPSAiT1RVX0lEIikgJT4lCiAgZ3JvdXBfYnkoQ2x1c3RlciwgU2FtcGxlSUQpICU+JSAKICBzdW1tYXJpc2UoQWdnUmVsQWIgPSBzdW0oQ291bnQpKSAlPiUgCiAgaW5uZXJfam9pbihycy5tYXAsIGJ5ID0gIlNhbXBsZUlEIikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IHN0cl9yZXBsYWNlKFRyZWF0bWVudCwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKQoKcnMuYWdnLm1lYW5zIDwtIHJzLmFnZy5yYSAlPiUgCiAgZ3JvdXBfYnkoVHJlYXRtZW50LCBDb21wYXJ0bWVudCwgVGltZSwgQ2x1c3RlcikgJT4lIAogIG11dGF0ZShNZWFuUmVsQWIgPSBtZWFuKEFnZ1JlbEFiKSkgJT4lIAogIHVuZ3JvdXAoKSAKCnJzLmNsc3RyLnAgPC0gcnMuYWdnLnJhICU+JSAKICBnZ3Bsb3QoYWVzKEFnZSwgQWdnUmVsQWIsIGNvbG9yID0gVHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgYWxwaGEgPSAwLjUsIHNoYXBlID0gMSkgKwogIGdlb21fbGluZShkYXRhID0gcnMuYWdnLm1lYW5zLCBhZXMoeSA9IE1lYW5SZWxBYiksIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gd3MubGluZSwgbGluZXR5cGUgPSAzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRydC5wYWwsIG5hbWUgPSAiVHJlYXRtZW50IikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICB5bGFiKCJSZWxhdGl2ZSBBYnVuZGFuY2UiKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICBmYWNldF93cmFwKH4gQ2x1c3RlciwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQoKcnMuY2xzdHIucApgYGAKCmBgYHtyfQpwbG90X2dyaWQocnMuaG0ucCwgcnMuY2xzdHIucCwKICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgcmVsX3dpZHRocyA9IGMoNCwzKSwKICAgICAgICAgIGFsaWduID0gImgiLAogICAgICAgICAgYXhpcyA9ICJiIiwKICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIpLAogICAgICAgICAgbGFiZWxfc2l6ZSA9IDIwKQpgYGAKCgpFTkRPU1BIRVJFCgp6LXRyYW5zZm9ybSByZWxhdGl2ZSBhYnVuZGFuY2VzLCBjYWxjdWxhdGUgdGhlIG1lYW4gei1zY29yZSBmb3IgZWFjaCB0cmVhdG1lbnQsIGFuZCB0aW1lIHBvaW50IGNvbWJpbmF0aW9uLCBnZW5lcmF0ZSBhIG1hdHJpeCBmb3IgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKYGBge3J9CmVzLnpzLnRpZHkgPC0gZXMub3R1LnRpZHkgJT4lCiAgaW5uZXJfam9pbihtYXAsIGJ5ID0gIlNhbXBsZUlEIikgJT4lCiAgZmlsdGVyKE9UVV9JRCAlaW4lIGVzLnNpZyRPVFVfSUQpICU+JQogIGdyb3VwX2J5KE9UVV9JRCkgJT4lCiAgbXV0YXRlKHpzY29yZSA9IChDb3VudCAtIG1lYW4oQ291bnQpKS9zZChDb3VudCkpICU+JQogIGdyb3VwX2J5KFRyZWF0bWVudCwgVGltZSwgT1RVX0lELCBBZ2UpICU+JQogIHN1bW1hcmlzZShNZWFuWlMgPSBtZWFuKHpzY29yZSkpICU+JQogIHVuZ3JvdXAoKQoKZXMuenMubXR4IDwtIGVzLnpzLnRpZHkgJT4lIAogICAgbXV0YXRlKEdyb3VwID0gcGFzdGUoVHJlYXRtZW50LCBUaW1lLCBzZXAgPSAiLiIpKSAlPiUgCiAgICBzZWxlY3QoR3JvdXAsIE1lYW5aUywgT1RVX0lEKSAlPiUgCiAgICBzcHJlYWQoa2V5ID0gR3JvdXAsIHZhbHVlID0gTWVhblpTKQplcy56cy5tdHggPC0gYXMuZGF0YS5mcmFtZShlcy56cy5tdHgpIApyb3duYW1lcyhlcy56cy5tdHgpIDwtIGVzLnpzLm10eCRPVFVfSUQgCmVzLnpzLm10eCA8LSBlcy56cy5tdHhbLC0xXSAKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmVzLmZjLm10eCA8LSBlcy5mYyAlPiUgCiAgc2VwYXJhdGUoQ29udHJhc3QsIGMoIlRyZWF0bWVudDIiLCAiVGltZSIpLCBzZXAgPSAiXFwuIikgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBUcmVhdG1lbnQyKSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudDIgIT0gIldDX1RSTiIpICU+JSAKICBmaWx0ZXIoT1RVX0lEICVpbiUgZXMuc2lnJE9UVV9JRCkgJT4lIAogIG11dGF0ZShDb250cmFzdCA9IHBhc3RlKFRyZWF0bWVudDIsIFRpbWUsIHNlcCA9ICIuIikpICU+JSAKICBzZWxlY3QoQ29udHJhc3QsIE9UVV9JRCwgZXN0aW1hdGUpICU+JSAKICBzcHJlYWQoa2V5ID0gQ29udHJhc3QsIHZhbHVlID0gZXN0aW1hdGUpCmVzLmZjLm10eCA8LSBhcy5kYXRhLmZyYW1lKGVzLmZjLm10eCkgCnJvd25hbWVzKGVzLmZjLm10eCkgPC0gZXMuZmMubXR4JE9UVV9JRCAKZXMuZmMubXR4IDwtIGVzLmZjLm10eFssLTFdIApgYGAKCkNob29zZSBhIGNsdXN0ZXJpbmcgbWV0aG9kIGFuZCBudW1iZXIgb2YgY2x1c3RlcnMKYGBge3J9CmZ2aXpfbmJjbHVzdChlcy56cy5tdHgsIEZVTiA9IGhjdXQsIG1ldGhvZCA9ICJ3c3MiKQoKZXMuYWMgPC0gZnVuY3Rpb24oeCkgewogIGFnbmVzKGVzLnpzLm10eCwgbWV0aG9kID0geCkkYWMKfQptYXBfZGJsKG0sIGVzLmFjKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNlZSB3aGF0IHZhbHVlcyBvZiBrIG1pZ2h0IGJlIHdvcnRoIHRlc3RpbmcKZnZpel9uYmNsdXN0KGVzLmZjLm10eCwgRlVOID0gaGN1dCwgbWV0aG9kID0gIndzcyIpCgojIFNlZSB3aGljaCBtZXRob2QgZ2V0cyB5b3UgdGhlIGJlc3QgY2x1c3RlcmluZwplcy5hYyA8LSBmdW5jdGlvbih4KSB7CiAgYWduZXMoZXMuZmMubXR4LCBtZXRob2QgPSB4KSRhYwp9Cm1hcF9kYmwobSwgZXMuYWMpCmBgYAoKUGVyZm9ybSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbmQgZ2V0IHRoZSBzZXQgb2YgY2x1c3RlcnMgYW5kIG9yZGVyIG9mIE9UVXMKYGBge3J9CmVzLmsgPC0gNgoKCmVzLmRpc3QgPC0gZGlzdChhcy5tYXRyaXgoZXMuenMubXR4KSkgCmVzLmNsdXN0IDwtIGhjbHVzdChlcy5kaXN0LCBtZXRob2QgPSAid2FyZC5EIikgCmVzLm9yZC5uYW1lcyA8LSBlcy5jbHVzdCRsYWJlbHNbZXMuY2x1c3Qkb3JkZXJdIAplcy5vcmQgPC0gZGF0YS5mcmFtZShPVFVfSUQgPSBlcy5vcmQubmFtZXMsIG9yZGVyID0gMTpsZW5ndGgoZXMub3JkLm5hbWVzKSkKCmVzLmN1dCA8LSBjdXRyZWUoZXMuY2x1c3RbYygxLDIsNCldLCBrID0gZXMuaykKZXMub3JkJENsdXN0ZXIgPC0gYXMuZmFjdG9yKGVzLmN1dFtlcy5vcmQkT1RVX0lEXSkKCmVzLm9yZCA8LSBlcy5vcmQgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoIkMiLCBDbHVzdGVyLCBzZXAgPSAiIikpICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgbXV0YXRlKG5PVFUgPSBuKCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShDbHVzdGVyMiA9IHBhc3RlKENsdXN0ZXIsICIgKCIsIG5PVFUsICIgT1RVcykiLCBzZXAgPSAiIikpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgplcy5rIDwtIDUKCmVzLmRpc3QgPC0gZGlzdChhcy5tYXRyaXgoZXMuZmMubXR4KSkgCmVzLmNsdXN0IDwtIGhjbHVzdChlcy5kaXN0LCBtZXRob2QgPSAid2FyZC5EIikgCmVzLm9yZC5uYW1lcyA8LSBlcy5jbHVzdCRsYWJlbHNbZXMuY2x1c3Qkb3JkZXJdIAplcy5vcmQgPC0gZGF0YS5mcmFtZShPVFVfSUQgPSBlcy5vcmQubmFtZXMsIG9yZGVyID0gMTpsZW5ndGgoZXMub3JkLm5hbWVzKSkKCmVzLmN1dCA8LSBjdXRyZWUoZXMuY2x1c3RbYygxLDIsNCldLCBrID0gZXMuaykKZXMub3JkJENsdXN0ZXIgPC0gYXMuZmFjdG9yKGVzLmN1dFtlcy5vcmQkT1RVX0lEXSkKCmVzLm9yZCA8LSBlcy5vcmQgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoIkMiLCBDbHVzdGVyLCBzZXAgPSAiIikpICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgbXV0YXRlKG5PVFUgPSBuKCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShDbHVzdGVyMiA9IHBhc3RlKENsdXN0ZXIsICIgKCIsIG5PVFUsICIgT1RVcykiLCBzZXAgPSAiIikpCmBgYAoKVmlzdWFsaXplIGFzIGhlYXRtYXAKYGBge3J9CmVzLmhtLnAgPC0gZXMuenMudGlkeSAlPiUgCiAgaW5uZXJfam9pbihlcy5vcmQsIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gc3RyX3JlcGxhY2UoVHJlYXRtZW50LCAiRCIsICJEUyIpKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IGZjdF9yZWxldmVsKFRyZWF0bWVudCwgIldDIikpICU+JSAKICBnZ3Bsb3QoYWVzKFRpbWUqMTAsIHJlb3JkZXIoT1RVX0lELCBvcmRlciksIGZpbGwgPSBNZWFuWlMpKSArCiAgZ2VvbV90aWxlKCkgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBJbmlUcmVhdG1lbnQqMTApLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBFbmRUcmVhdG1lbnQqMTApLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiTWVhbiBSZWwuIEFidW5kLlxuKHotc2NvcmUpIikgKwogICNzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogIHlsYWIoIkRpZmZlcmVudGlhbGx5IEFidW5kYW50IE9UVSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X2dyaWQoQ2x1c3RlciB+IFRyZWF0bWVudCwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAxKQoKZXMuaG0ucAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmVzLmZjICU+JSAKICBzZXBhcmF0ZShDb250cmFzdCwgYygiVHJlYXRtZW50MiIsICJUaW1lIiksIHNlcCA9ICJcXC4iKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IFRyZWF0bWVudDIpICU+JSAKICBmaWx0ZXIoVHJlYXRtZW50MiAhPSAiV0NfVFJOIikgJT4lIAogIG11dGF0ZShUaW1lID0gYXMubnVtZXJpYyhUaW1lKSkgJT4lIAogIGlubmVyX2pvaW4oZXMub3JkLCBieSA9ICJPVFVfSUQiKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IHN0cl9yZXBsYWNlKFRyZWF0bWVudCwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKSAlPiUgCiAgbXV0YXRlKGVzdGltYXRlMiA9IGlmZWxzZShhYnMoZXN0aW1hdGUpID41LCA1ICogc2lnbihlc3RpbWF0ZSksIGVzdGltYXRlKSkgJT4lIAogIGdncGxvdChhZXMoVGltZSoxMCwgcmVvcmRlcihPVFVfSUQsIG9yZGVyKSwgZmlsbCA9IGVzdGltYXRlMikpICsKICBnZW9tX3RpbGUoKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEluaVRyZWF0bWVudCoxMCksIGxpbmV0eXBlID0gMywgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEVuZFRyZWF0bWVudCoxMCksIGxpbmV0eXBlID0gMywgY29sb3IgPSAid2hpdGUiKSArCiAgI3NjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiTWVhbiBSZWwuIEFidW5kLlxuKHotc2NvcmUpIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKG5hbWUgPSAibG9nMkZDIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMTEsICJCckJHIikpICsKICAjc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICB5bGFiKCJEaWZmZXJlbnRpYWxseSBBYnVuZGFudCBPVFUiKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICBmYWNldF9ncmlkKENsdXN0ZXIgfiBUcmVhdG1lbnQsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlLmFsaWduID0gMSkKYGBgCgpWaXN1YWxpemUgdGhlIGFnZ3JlZ2F0ZWQgcmVsYXRpdmUgYWJ1bmRhbmNlcwpgYGB7cn0KZXMuYWdnLnJhIDwtIGVzLm90dS50aWR5ICU+JSAKICBpbm5lcl9qb2luKGVzLm9yZCwgYnkgPSAiT1RVX0lEIikgJT4lCiAgZ3JvdXBfYnkoQ2x1c3RlciwgU2FtcGxlSUQpICU+JSAKICBzdW1tYXJpc2UoQWdnUmVsQWIgPSBzdW0oQ291bnQpKSAlPiUgCiAgaW5uZXJfam9pbihlcy5tYXAsIGJ5ID0gIlNhbXBsZUlEIikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IHN0cl9yZXBsYWNlKFRyZWF0bWVudCwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKQoKZXMuYWdnLm1lYW5zIDwtIGVzLmFnZy5yYSAlPiUgCiAgZ3JvdXBfYnkoVHJlYXRtZW50LCBDb21wYXJ0bWVudCwgVGltZSwgQ2x1c3RlcikgJT4lIAogIG11dGF0ZShNZWFuUmVsQWIgPSBtZWFuKEFnZ1JlbEFiKSkgJT4lIAogIHVuZ3JvdXAoKSAKCmVzLmNsc3RyLnAgPC0gZXMuYWdnLnJhICU+JSAKICBnZ3Bsb3QoYWVzKEFnZSwgQWdnUmVsQWIsIGNvbG9yID0gVHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgYWxwaGEgPSAwLjUsIHNoYXBlID0gMSkgKwogIGdlb21fbGluZShkYXRhID0gZXMuYWdnLm1lYW5zLCBhZXMoeSA9IE1lYW5SZWxBYiksIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gd3MubGluZSwgbGluZXR5cGUgPSAzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRydC5wYWwsIG5hbWUgPSAiVHJlYXRtZW50IikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsgIAogIHlsYWIoIlJlbGF0aXZlIEFidW5kYW5jZSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X3dyYXAofiBDbHVzdGVyLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCgplcy5jbHN0ci5wCmBgYAoKYGBge3J9CnBsb3RfZ3JpZChlcy5obS5wLCBlcy5jbHN0ci5wLAogICAgICAgICAgbnJvdyA9IDEsCiAgICAgICAgICByZWxfd2lkdGhzID0gYygzLDIpLAogICAgICAgICAgYWxpZ24gPSAiaCIsCiAgICAgICAgICBheGlzID0gImIiLAogICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICBsYWJlbF9zaXplID0gMjApCmBgYAoKCmBgYHtyfQphbGwub3JkIDwtIHJiaW5kKG11dGF0ZShycy5vcmQsIENvbXBhcnRtZW50ID0gIlJTIiksCiAgICAgICAgICAgICAgICAgbXV0YXRlKGVzLm9yZCwgQ29tcGFydG1lbnQgPSAiRVMiKSkKCnJiaW5kKG11dGF0ZShycy5mYywgQ29tcGFydG1lbnQgPSAiUlMiKSwKICAgICAgbXV0YXRlKGVzLmZjLCBDb21wYXJ0bWVudCA9ICJFUyIpKSAlPiUgCiAgc2VwYXJhdGUoQ29udHJhc3QsIGMoIlRyZWF0bWVudDIiLCAiVGltZSIpLCBzZXAgPSAiXFwuIiwgcmVtb3ZlID0gRikgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBUcmVhdG1lbnQyKSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudDIgIT0gIldDX1RSTiIpICU+JSAKICBtdXRhdGUoVGltZSA9IGFzLm51bWVyaWMoVGltZSkpICU+JSAKICBpbm5lcl9qb2luKGFsbC5vcmQsIGJ5ID0gYygiQ29tcGFydG1lbnQiLCAiT1RVX0lEIikpICU+JSAKICBtdXRhdGUoQ29udHJhc3QgPSBjYXNlX3doZW4oVHJlYXRtZW50ID09ICJEMSIgfiAiV0MgdnMgRFMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID09ICJEMiIgfiAiV0MgdnMgRFMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID09ICJEMyIgfiAiV0MgdnMgRFMzIikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVsZXZlbChDb21wYXJ0bWVudCwgIlJTIikpICU+JSAKICBtdXRhdGUoZXN0aW1hdGUyID0gaWZlbHNlKGFicyhlc3RpbWF0ZSkgPjQsIDQgKiBzaWduKGVzdGltYXRlKSwgZXN0aW1hdGUpKSAlPiUgCiAgZ2dwbG90KGFlcyhUaW1lKjEwLCByZW9yZGVyKE9UVV9JRCwgb3JkZXIpLCBmaWxsID0gZXN0aW1hdGUyKSkgKwogIGdlb21fdGlsZSgpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gSW5pVHJlYXRtZW50ICogMTApLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gRW5kVHJlYXRtZW50ICogMTApLCBsaW5ldHlwZSA9IDMpICsKICAjc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZSA9ICJNZWFuIFJlbC4gQWJ1bmQuXG4oei1zY29yZSkiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4obmFtZSA9ICJsb2cyRkMiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMSwgIkJyQkciKSkgKwogICNzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogIHlsYWIoIkRpZmZlcmVudGlhbGx5IEFidW5kYW50IE9UVSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X2dyaWQoQ29tcGFydG1lbnQgKyBDbHVzdGVyIH4gQ29udHJhc3QsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlLmFsaWduID0gMSkKCmFsbC5vcmQgJT4lIAogIGlubmVyX2pvaW4odGF4LCBieSA9ICJPVFVfSUQiKSAlPiUgCiAgZ2dwbG90KGFlcygxLCBmaWxsID0gUGh5Q2xhc3MyKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIikgKwogIGZhY2V0X2dyaWQoQ29tcGFydG1lbnQgKyBDbHVzdGVyIH4gLiwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiVGF4b24iLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHBoeS5wYWxbYygxMToxNCwyOjEwLDEpXSwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsZXZlbHModGF4JFBoeUNsYXNzMilbYygxMToxNCwyOjEwLDEpXSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAxKQpgYGAKCgpHZW5lcmF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgY2x1c3RlcmluZyByZXN1bHRzLCBhbmQgbGFiZWwgdGhlIGludGVyZXN0aW5nIGNsdXN0ZXJzIGZvciBkb3duc3RyZWFtYSBhbmFseXNlcwpHZW5lYXJ0ZSBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgdGhlIG1lYW4gei1zY29yZXMgZm9yIHBsb3R0aW5nCmBgYHtyfQphbGwuY2x1c3Quc3VtbWFyeSA8LSByYmluZChtdXRhdGUocnMub3JkLCBDb21wYXJ0bWVudCA9ICJSUyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGVzLm9yZCwgQ29tcGFydG1lbnQgPSAiRVMiKSkgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoQ29tcGFydG1lbnQsIENsdXN0ZXIsIHNlcCA9ICIiKSkgJT4lCiAgZmlsdGVyKENsdXN0ZXIgJWluJSBjKCJSU0MxIiwgIlJTQzIiLCAiUlNDNCIsICJFU0MzIiwgIkVTQzUiKSkgJT4lIAogIG11dGF0ZShUcmVuZCA9IGNhc2Vfd2hlbihDbHVzdGVyID09ICJSU0MxIiB+ICJUcmFuc2llbnQgRW5yaWNobWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPT0gIlJTQzIiIH4gIlBlcnNpc3RlbnQgRGVwbGV0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3RlciA9PSAiUlNDNCIgfiAiVHJhbnNpZW50IERlcGxldGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPT0gIkVTQzMiIH4gIlNlbWktUGVyc2lzdGVudCBFbnJpY2htZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3RlciA9PSAiRVNDNSIgfiAiUGVyc2lzdGVudCBEZXBsZXRpb24iKSkgCgphbGwuY2x1c3Rlci50b3RhbCA8LSBhbGwuY2x1c3Quc3VtbWFyeSAlPiUgCiAgZ3JvdXBfYnkoQ2x1c3Rlciwgbk9UVSwgQ2x1c3RlcjIsIENvbXBhcnRtZW50LCBUcmVuZCkgJT4lIAogIGNvdW50KCkgJT4lIAogIHNlbGVjdCgtbikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmhpem9zcGhlcmUiID0gIlJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvc3BoZXJlIiA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKQoKYWxsLmFnZy5yYSA8LSByYmluZChtdXRhdGUocnMuYWdnLnJhLCBDb21wYXJ0bWVudCA9ICJSUyIpLAogICAgICAgICAgICAgICAgICAgIG11dGF0ZShlcy5hZ2cucmEsIENvbXBhcnRtZW50ID0gIkVTIikpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IHBhc3RlKENvbXBhcnRtZW50LCBDbHVzdGVyLCBzZXAgPSAiIikpCgpzYXZlUkRTKGFsbC5jbHVzdC5zdW1tYXJ5LCAiLi4vRGF0YS9kcm91Z2h0X2NsdXN0ZXJzLlJEUyIpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgphbGwuY2x1c3Quc3VtbWFyeSA8LSByYmluZChtdXRhdGUocnMub3JkLCBDb21wYXJ0bWVudCA9ICJSUyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGVzLm9yZCwgQ29tcGFydG1lbnQgPSAiRVMiKSkgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoQ29tcGFydG1lbnQsIENsdXN0ZXIsIHNlcCA9ICIiKSkgJT4lCiAgZmlsdGVyKENsdXN0ZXIgJWluJSBjKCJSU0MxIiwgIlJTQzIiLCAiUlNDNCIsICJFU0MzIiwgIkVTQzQiKSkgJT4lIAogIG11dGF0ZShUcmVuZCA9IGNhc2Vfd2hlbihDbHVzdGVyID09ICJSU0MxIiB+ICJUcmFuc2llbnQgRW5yaWNobWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPT0gIlJTQzIiIH4gIlRyYW5zaWVudCBEZXBsZXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBDbHVzdGVyID09ICJSU0M0IiB+ICJQZXJzaXN0ZW50IERlcGxldGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPT0gIkVTQzMiIH4gIlNlbWktUGVyc2lzdGVudCBFbnJpY2htZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3RlciA9PSAiRVNDNCIgfiAiUGVyc2lzdGVudCBEZXBsZXRpb24iKSkgCgphbGwuY2x1c3Rlci50b3RhbCA8LSBhbGwuY2x1c3Quc3VtbWFyeSAlPiUgCiAgZ3JvdXBfYnkoQ2x1c3Rlciwgbk9UVSwgQ2x1c3RlcjIsIENvbXBhcnRtZW50LCBUcmVuZCkgJT4lIAogIGNvdW50KCkgJT4lIAogIHNlbGVjdCgtbikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmhpem9zcGhlcmUiID0gIlJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvc3BoZXJlIiA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKQoKYWxsLmFnZy5yYSA8LSByYmluZChtdXRhdGUocnMuYWdnLnJhLCBDb21wYXJ0bWVudCA9ICJSUyIpLAogICAgICAgICAgICAgICAgICAgIG11dGF0ZShlcy5hZ2cucmEsIENvbXBhcnRtZW50ID0gIkVTIikpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IHBhc3RlKENvbXBhcnRtZW50LCBDbHVzdGVyLCBzZXAgPSAiIikpCgpzYXZlUkRTKGFsbC5jbHVzdC5zdW1tYXJ5LCAiLi4vRGF0YS9kcm91Z2h0X2NsdXN0ZXJzLlJEUyIpCmBgYAoKYGBge3J9CmFsbC5jbHVzdGVyLm1lYW5zIDwtIGFsbC5hZ2cucmEgJT4lCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmhpem9zcGhlcmUiID0gIlJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvc3BoZXJlIiA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKSAlPiUgCiAgaW5uZXJfam9pbihzZWxlY3QoYWxsLmNsdXN0ZXIudG90YWwsIC1Db21wYXJ0bWVudCksIGJ5ID0gIkNsdXN0ZXIiKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRpbWUsIEFnZSwgVHJlYXRtZW50MiwgVHJlbmQpICU+JSAKICBzdW1tYXJpc2UoTWVhblJlbEFiID0gbWVhbihBZ2dSZWxBYikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQyID0gc3RyX3JlcGxhY2UoVHJlYXRtZW50MiwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQyID0gZmN0X3JlbGV2ZWwoVHJlYXRtZW50MiwgIldDIikpIAoKYWxsLmNsdXN0ZXIuYWIgPC0gYWxsLmFnZy5yYSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmhpem9zcGhlcmUiID0gIlJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvc3BoZXJlIiA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKSAlPiUgCiAgaW5uZXJfam9pbihzZWxlY3QoYWxsLmNsdXN0ZXIudG90YWwsIC1Db21wYXJ0bWVudCksIGJ5ID0gIkNsdXN0ZXIiKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudDIgPSBzdHJfcmVwbGFjZShUcmVhdG1lbnQyLCAiRCIsICJEUyIpKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudDIgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQyLCAiV0MiKSkKCmFsbC5jbHVzdGVyLnRheCA8LSBhbGwuY2x1c3Quc3VtbWFyeSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIENsdXN0ZXIsIE9UVV9JRCkgJT4lIAogIGNvdW50KCkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgaW5uZXJfam9pbih0YXgsIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBpbm5lcl9qb2luKHNlbGVjdChhbGwuY2x1c3Quc3VtbWFyeSwgT1RVX0lELCBUcmVuZCwgQ29tcGFydG1lbnQpLCBieSA9IGMoIkNvbXBhcnRtZW50IiwgIk9UVV9JRCIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmhpem9zcGhlcmUiID0gIlJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvc3BoZXJlIiA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSaGl6b3NwaGVyZSIpKQpgYGAKCmBgYHtyfQphbGwuYWIgPC0gYWxsLmNsdXN0ZXIuYWIgJT4lIAogIGdncGxvdChhZXMoQWdlLCBBZ2dSZWxBYiwgY29sb3IgPSBUcmVhdG1lbnQyKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAxLCBzaGFwZSA9IDEsIHNpemUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gd3MubGluZSwgbGluZXR5cGUgPSAzKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBhbGwuY2x1c3Rlci5tZWFucywgYWVzKHkgPSBNZWFuUmVsQWIpLCBzaXplID0gMSkgKyAKICB5bGFiKCJDdW11bGF0aXZlIFJlbGF0aXZlIEFidW5kYW5jZSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X3dyYXAofiBDb21wYXJ0bWVudCArIFRyZW5kLCBzY2FsZXMgPSAiZnJlZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdHJ0LnBhbCwgbmFtZSA9ICJUcmVhdG1lbnQiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjgsMC4yKSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCgphbGwuYWIKCmFsbC50YXgucCA8LSBhbGwuY2x1c3Rlci50YXggJT4lIAogIGdyb3VwX2J5KENvbXBhcnRtZW50LCBUcmVuZCwgUGh5Q2xhc3MyKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gc3VtKG4pKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRyZW5kKSAlPiUgCiAgbXV0YXRlKEZyYWN0aW9uID0gQ291bnQgLyBzdW0oQ291bnQpKSAlPiUgCiAgbXV0YXRlKHltYXggPSBjdW1zdW0oRnJhY3Rpb24pLAogICAgICAgICBuUGh5ID0gbigpKSAlPiUgCiAgbXV0YXRlKHltaW4gPSBjKDAsIHltYXhbMTpuUGh5IC0gMV0pKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcmVjdChhZXMoeW1heD15bWF4LCB5bWluPXltaW4sIHhtYXg9IDQsIHhtaW49IDMsIGZpbGw9IFBoeUNsYXNzMikpICsKICBnZW9tX3RleHQoZGF0YSA9IGFsbC5jbHVzdGVyLnRvdGFsLCBhZXMoMiwgMCwgbGFiZWwgPSBuT1RVKSwgc2l6ZSA9IDcpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIlRheG9uIiwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBwaHkucGFsW2MoMTE6MTQsMjoxMCwxKV0sCiAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gbGV2ZWxzKHRheCRQaHlDbGFzczIpW2MoMTE6MTQsMjoxMCwxKV0pICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gNCkpICsKICBjb29yZF9wb2xhcih0aGV0YT0ieSIpICsgCiAgeGxpbShjKDIsIDQpKSArCiAgZmFjZXRfd3JhcCguIH4gQ29tcGFydG1lbnQgKyBUcmVuZCwgbnJvdyA9IDEpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF9ibGFuaygpKQoKYWxsLnRheC5wCgphbGwuY2x1c3QucCA8LSBwbG90X2dyaWQoYWxsLmFiLCBhbGwudGF4LnAsIAogICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoNiw0KSwKICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduID0gInYiLAogICAgICAgICAgICAgICAgICAgICAgICAgYXhpcyA9ICJsciIsCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSAiQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbF9zaXplID0gMjApCgpwbG90X2dyaWQoZGFvLnAsIGFsbC5hYiwgYWxsLnRheC5wLCAKICAgICAgICAgIG5jb2wgPSAxLCAKICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLDIsMSksCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiLCBOQSksCiAgICAgICAgICBsYWJlbF9zaXplID0gMjApICMjIzc2Njo5MzYKYGBgCgpgYGB7cn0KYWxsLmNsdXN0ZXIudGF4ICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgVHJlbmQsIFBoeWx1bSwgUGh5Q2xhc3MyLCBDbGFzcywgT3JkZXIpICU+JSAKICBjb3VudCgpICU+JSAKICBmaWx0ZXIobiA+IDEpICU+JSAKICBtdXRhdGUoVGF4b25vbXkgPSBwYXN0ZShQaHlsdW0sIENsYXNzLCBPcmRlciwgc2VwID0gIiAvICIpKSAlPiUgCiAgZ2dwbG90KGFlcyhUcmVuZCwgVGF4b25vbXksIGZpbGwgPSBQaHlDbGFzczIpKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDEpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbikpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIlRheG9uIiwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBwaHkucGFsLAogICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGxldmVscyh0YXgkUGh5Q2xhc3MyKSkgKwogIGZhY2V0X2dyaWQoLiB+IENvbXBhcnRtZW50LCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgoKYGBge3J9CnN1cHAuY2x1c3Quc3VtbWFyeSA8LSByYmluZChtdXRhdGUocnMub3JkLCBDb21wYXJ0bWVudCA9ICJSUyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGVzLm9yZCwgQ29tcGFydG1lbnQgPSAiRVMiKSkgJT4lIAogIG11dGF0ZShDbHVzdGVyID0gcGFzdGUoQ29tcGFydG1lbnQsIENsdXN0ZXIsIHNlcCA9ICIiKSkgJT4lCiAgI2ZpbHRlcihDbHVzdGVyICVpbiUgYygiUlNDMSIsICJSU0MyIiwgIlJTQzQiLCAiRVNDMyIsICJFU0M0IikpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBjYXNlX3doZW4oQ2x1c3RlciA9PSAiUlNDMSIgfiAiVHJhbnNpZW50IEVucmljaG1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBDbHVzdGVyID09ICJSU0MyIiB+ICJUcmFuc2llbnQgRGVwbGV0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3RlciA9PSAiUlNDNCIgfiAiUGVyc2lzdGVudCBEZXBsZXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBDbHVzdGVyID09ICJFU0MzIiB+ICJTZW1pLVBlcnNpc3RlbnQgRW5yaWNobWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPT0gIkVTQzQiIH4gIlBlcnNpc3RlbnQgRGVwbGV0aW9uIikpICU+JSAKICBzZWxlY3QoQ29tcGFydG1lbnQsIENsdXN0ZXIsIFRyZW5kLCBPVFVfSUQpICU+JSAKICBpbm5lcl9qb2luKHRheCwgYnkgPSAiT1RVX0lEIikgJT4lIAogIHNlbGVjdCgtUGh5Q2xhc3MsIC1QaHlDbGFzczIpIAoKd3JpdGUudGFibGUoc3VwcC5jbHVzdC5zdW1tYXJ5LCAiLi4vVGFibGVzL2Rhb19jbHN0ci50c3YiLCBzZXAgPSAiXHQiLCBxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYpCmBgYAoK